package com.zzh.partnersys.search.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch._types.query_dsl.ScriptQuery;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.json.JsonData;
import com.zzh.partnersys.search.entity.TeamDocument;
import com.zzh.partnersys.search.entity.UserDocument;
import com.zzh.partnersys.search.entity.vo.PageResult;
import com.zzh.partnersys.search.entity.vo.TeamSearchVO;
import com.zzh.partnersys.search.entity.vo.UserSearchVO;
import com.zzh.partnersys.search.service.ElasticSearchSearchService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author: zzh
 * @date: 2025/11/15 16:35:42
 * @version: 1.0
 */
@Slf4j
@Service
public class ElasticSearchSearchServiceImpl implements ElasticSearchSearchService {

    private static final String USER_INDEX = "user_index";
    private static final String TEAM_INDEX = "team_index";

    @Resource
    private ElasticsearchClient elasticsearchClient;

    @Override
    public PageResult<UserSearchVO> recommendUsers(Long currentUserId, Integer pageNum, Integer pageSize) {
        try {
            // 构建 more_like_this 查询
            Query moreLikeThisQuery = Query.of(q -> q
                    .moreLikeThis(m -> m
                            .fields("tags^3", "personalProfile^1")
                            .like(l -> l.document(d -> d
                                    .index(USER_INDEX)
                                    .id(String.valueOf(currentUserId))))
                            .minTermFreq(1)
                            .maxQueryTerms(20)
                            .minimumShouldMatch("60%")
                    )
            );

            // 构建过滤条件
            BoolQuery boolQuery = BoolQuery.of(b -> b
                    .must(moreLikeThisQuery)
                    .filter(f -> f.term(t -> t.field("userStatus").value(0)))  // 仅正常用户
                    .filter(f -> f.term(t -> t.field("userRole").value(0)))     // 仅普通用户
                    .filter(f -> f.term(t -> t.field("isDelete").value(0)))     // 未删除
                    .mustNot(mn -> mn.term(t -> t.field("id").value(currentUserId)))  // 排除自己
            );

            // 构建搜索请求
            SearchRequest searchRequest = SearchRequest.of(s -> s
                    .index(USER_INDEX)
                    .query(Query.of(q -> q.bool(boolQuery)))
                    .from(pageNum * pageSize)
                    .size(pageSize)
                    .sort(so -> so.score(sc -> sc.order(co.elastic.clients.elasticsearch._types.SortOrder.Desc)))
                    .sort(so -> so.field(f -> f.field("username.keyword").order(co.elastic.clients.elasticsearch._types.SortOrder.Asc)))
            );

            // 执行搜索
            SearchResponse<UserDocument> response = elasticsearchClient.search(searchRequest, UserDocument.class);

            // 转换结果
            List<UserSearchVO> userList = response.hits().hits().stream()
                    .map(hit -> {
                        UserDocument doc = hit.source();
                        UserSearchVO vo = new UserSearchVO();
                        vo.setId(doc.getId());
                        vo.setUsername(doc.getUsername());
                        vo.setAvatarUrl(doc.getAvatarUrl());
                        vo.setGender(doc.getGender());
                        vo.setTags(doc.getTags());
                        vo.setPersonalProfile(doc.getPersonalProfile());
                        vo.setScore(hit.score());
                        return vo;
                    })
                    .collect(Collectors.toList());

            return new PageResult<>(
                    userList,
                    response.hits().total().value(),
                    (long) pageNum,
                    (long) pageSize
            );

        } catch (Exception e) {
            log.error("推荐用户查询失败，currentUserId: {}", currentUserId, e);
            return new PageResult<>(new ArrayList<>(), 0L, (long) pageNum, (long) pageSize);
        }
    }

    @Override
    public PageResult<TeamSearchVO> searchTeams(String keyword, Integer pageNum, Integer pageSize) {
        try {
            BoolQuery.Builder boolQueryBuilder = new BoolQuery.Builder();

            // 如果有关键字，添加 multi_match 查询
            if (StringUtils.hasText(keyword)) {
                Query multiMatchQuery = Query.of(q -> q
                        .multiMatch(m -> m
                                .query(keyword)
                                .fields("name^3", "collabGoal^2", "description^2", "tags^1")
                                .type(co.elastic.clients.elasticsearch._types.query_dsl.TextQueryType.BestFields)
                                .operator(co.elastic.clients.elasticsearch._types.query_dsl.Operator.Or)
                                .fuzziness("AUTO")
                        )
                );
                boolQueryBuilder.must(multiMatchQuery);
            }

            // 构建过滤条件
            boolQueryBuilder
                    .filter(f -> f.term(t -> t.field("status").value(0)))  // 公开队伍
                    .filter(f -> f.term(t -> t.field("isDelete").value(0)))  // 未删除
                    // 未满员（currentTeamNum < maxNum）
                    .filter(f -> f.script(ScriptQuery.of(s -> s
                            .script(sc -> sc.inline(i -> i
                                    .source("doc['currentTeamNum'].value < doc['maxNum'].value")
                            ))
                    )))
                    // 未过期或永久有效
                    .filter(f -> f.bool(b -> b
                            .should(sh -> sh.range(r -> r
                                    .field("expireTime")
                                    .gte(JsonData.of("now"))
                            ))
                            .should(sh -> sh.bool(bb -> bb
                                    .mustNot(mn -> mn.exists(e -> e.field("expireTime")))
                            ))
                            .minimumShouldMatch("1")
                    ));

            // 构建搜索请求
            SearchRequest searchRequest = SearchRequest.of(s -> s
                    .index(TEAM_INDEX)
                    .query(Query.of(q -> q.bool(boolQueryBuilder.build())))
                    .from(pageNum * pageSize)
                    .size(pageSize)
                    .sort(so -> so.score(sc -> sc.order(co.elastic.clients.elasticsearch._types.SortOrder.Desc)))
                    .sort(so -> so.field(f -> f.field("currentTeamNum").order(co.elastic.clients.elasticsearch._types.SortOrder.Asc)))
            );

            // 执行搜索
            SearchResponse<TeamDocument> response = elasticsearchClient.search(searchRequest, TeamDocument.class);

            // 转换结果
            List<TeamSearchVO> teamList = response.hits().hits().stream()
                    .map(hit -> {
                        TeamDocument doc = hit.source();
                        TeamSearchVO vo = new TeamSearchVO();
                        vo.setId(doc.getId());
                        vo.setName(doc.getName());
                        vo.setDescription(doc.getDescription());
                        vo.setImageUrl(doc.getImageUrl());
                        vo.setCollabGoal(doc.getCollabGoal());
                        vo.setProjectCycle(doc.getProjectCycle());
                        vo.setTags(doc.getTags());
                        vo.setStatus(doc.getStatus());
                        vo.setCurrentTeamNum(doc.getCurrentTeamNum());
                        vo.setMaxNum(doc.getMaxNum());
                        vo.setExpireTime(doc.getExpireTime());
                        vo.setScore(hit.score());
                        return vo;
                    })
                    .collect(Collectors.toList());

            return new PageResult<>(
                    teamList,
                    response.hits().total().value(),
                    (long) pageNum,
                    (long) pageSize
            );

        } catch (Exception e) {
            log.error("搜索队伍失败，keyword: {}", keyword, e);
            return new PageResult<>(new ArrayList<>(), 0L, (long) pageNum, (long) pageSize);
        }
    }
}
